﻿using System;

namespace Grimgine.Math
{
    /// <summary>
    /// 
    /// </summary>
    public class Frustum : IEquatable<Frustum>
    {
        #region Fields

        /// <summary>
        /// 
        /// </summary>
        public Plane Top;

        /// <summary>
        /// 
        /// </summary>
        public Plane Bottom;

        /// <summary>
        /// 
        /// </summary>
        public Plane Left;

        /// <summary>
        /// 
        /// </summary>
        public Plane Right;

        /// <summary>
        /// 
        /// </summary>
        public Plane Near;

        /// <summary>
        /// 
        /// </summary>
        public Plane Far;

        #endregion

        #region Constructor

        /// <summary>
        /// 
        /// </summary>
        public Frustum()
        {
        }

        #endregion

        #region Frustum Methods

        /// <summary>
        /// 
        /// </summary>
        /// <param name="viewProj"></param>
        public void CalculateFrustum(Matrix4 viewProj)
        {
            Left.A = viewProj.M14 + viewProj.M11;
            Left.B = viewProj.M24 + viewProj.M21;
            Left.C = viewProj.M34 + viewProj.M31;
            Left.D = viewProj.M44 + viewProj.M41;
            Left.Normalize();

            Right.A = viewProj.M14 - viewProj.M11;
            Right.B = viewProj.M24 - viewProj.M21;
            Right.C = viewProj.M34 - viewProj.M31;
            Right.D = viewProj.M44 - viewProj.M41;
            Right.Normalize();

            Top.A = viewProj.M14 - viewProj.M12;
            Top.B = viewProj.M24 - viewProj.M22;
            Top.C = viewProj.M34 - viewProj.M32;
            Top.D = viewProj.M44 - viewProj.M42;
            Top.Normalize();

            Bottom.A = viewProj.M14 + viewProj.M12;
            Bottom.B = viewProj.M24 + viewProj.M22;
            Bottom.C = viewProj.M34 + viewProj.M32;
            Bottom.D = viewProj.M44 + viewProj.M42;
            Bottom.Normalize();

            Near.A = viewProj.M13;
            Near.B = viewProj.M23;
            Near.C = viewProj.M33;
            Near.D = viewProj.M43;
            Near.Normalize();

            Far.A = viewProj.M14 - viewProj.M13;
            Far.B = viewProj.M24 - viewProj.M23;
            Far.C = viewProj.M34 - viewProj.M33;
            Far.D = viewProj.M44 - viewProj.M43;
            Far.Normalize();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        public bool IsPointVisible(float x, float y, float z)
        {
            if ((Left.Distance(x, y, z) < 0) ||
                (Right.Distance(x, y, z) < 0) ||
                (Top.Distance(x, y, z) < 0) ||
                (Bottom.Distance(x, y, z) < 0) ||
                (Near.Distance(x, y, z) < 0) ||
                (Far.Distance(x, y, z) < 0))
                return false;

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <returns></returns>
        public bool IsPointVisible(Vector3 v1)
        {
            return IsPointVisible(v1.X, v1.Y, v1.Z);
        }

        #endregion

        #region IEquatable Implementation

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            return obj is Frustum && this == (Frustum)obj;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(Frustum other)
        {
            return (this == other);
        }

        #endregion

        #region Hash Code

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #endregion
    }
}
